﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace Moon.Sql
{
    public class MoonSqlProvider : IMoonSql
    {
        private IDatabase _Database = null;
        private string _connectionString = null;
        private DatabaseType _dbType;
        private string _ThreadId = string.Empty;
        private ThreadLocal<List<IDatabase>> _ContextList = new ThreadLocal<List<IDatabase>>();
        private int _CommandTimeout = 30;

        public MoonSqlProvider(string connectionString, DatabaseType dbType = DatabaseType.SqlServer, int commandTimeout = 30)
        {
            this._connectionString = connectionString;
            this._dbType = dbType;
            _ThreadId = Thread.CurrentThread.ManagedThreadId.ToString();
        }

        /// <summary>
        /// 执行前
        /// </summary>
        public Action<ExecutingArgs> OnExecuting { get; set; }
        /// <summary>
        /// 执行后
        /// </summary>
        public Action<ExecutedArgs> OnExecuted { get; set; }

        /// <summary>
        /// 数据库通用操作方法
        /// </summary>
        public IDatabase Database => GetDatabase();

        private IDatabase GetDatabase()
        {
            IDatabase result = _Database;
            if (_ThreadId == Thread.CurrentThread.ManagedThreadId.ToString())
            {
                //相同线程
                if (_Database == null)
                {
                    result = _Database = CreateDatabase();
                }
            }
            else
            {
                //不同线程
                result = GetContext();
            }
            return result;
        }

        private IDatabase GetContext()
        {
            if (_ContextList.Value == null)
            {
                var context = CreateDatabase();
                AddCallContext(context);
                return context;
            }
            else
            {
                var result = GetCallContext();
                if (result == null)
                {
                    var copy = CreateDatabase();
                    AddCallContext(copy);
                    return copy;
                }
                else
                {
                    return result;
                }
            }
        }

        private void AddCallContext(IDatabase context)
        {
            _ContextList.Value = new List<IDatabase>();
            _ContextList.Value.Add(context);
        }

        private IDatabase GetCallContext()
        {
            return _ContextList.Value.FirstOrDefault();
        }

        private IDatabase CreateDatabase()
        {
            var db = InstanceFactory.CreateInstance<IDatabase>(_dbType.ToString() + "Database", new object[] { _connectionString });
            db.CommandTimeout = _CommandTimeout;
            db.Aop = new AopProvider();
            db.Aop.OnExecuting = OnExecuting;
            db.Aop.OnExecuted = OnExecuted;
            return db;
        }

        #region Transaction
        /// <summary>
        /// 提供给跨库事务的方法 可直接用 RunTransaction
        /// </summary>
        /// <param name="isolationLevel"></param>
        public void BeginTransaction(IsolationLevel isolationLevel = IsolationLevel.ReadCommitted)
        {
            Database.BeginTransaction(isolationLevel);
        }
        /// <summary>
        /// 提供给跨库事务的方法 可直接用 RunTransaction
        /// </summary>
        /// <param name="isolationLevel"></param>
        public void CommitTransaction()
        {
            Database.CommitTransaction();
        }
        /// <summary>
        /// 提供给跨库事务的方法 可直接用 RunTransaction
        /// </summary>
        /// <param name="isolationLevel"></param>
        public void RollbackTransaction()
        {
            Database.RollbackTransaction();
        }
        /// <summary>
        /// 事务
        /// </summary>
        /// <param name="action"></param>
        /// <param name="isolationLevel"></param>
        /// <returns></returns>
        public RunTransactionResult RunTransaction(Action action, IsolationLevel isolationLevel = IsolationLevel.ReadCommitted)
        {
            return Database.RunTransaction(action, isolationLevel);
        }
        #endregion Transaction

        private IInsertable<T> _Insertable<T>()
        {
            IInsertable<T> insertable = InstanceFactory.CreateInsertable<T>(_dbType, Database);
            return insertable;
        }

        public IInsertable<T> Insertable<T>()
        {
            IInsertable<T> insertable = _Insertable<T>();
            return insertable.Insertable(null);
        }

        /// <summary>
        /// 添加
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="t"></param>
        /// <returns></returns>
        public IInsertable<T> Insertable<T>(T t)
        {
            IInsertable<T> insertable = _Insertable<T>();
            return insertable.Insertable(t);
        }

        /// <summary>
        /// 添加
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="list"></param>
        /// <returns></returns>
        public IInsertable<T> Insertable<T>(List<T> list)
        {
            IInsertable<T> insertable = _Insertable<T>();
            return insertable.Insertable(list);
        }

        private IUpdateable<T> _Updateable<T>()
        {
            IUpdateable<T> updateable = InstanceFactory.CreateUpdateable<T>(_dbType, Database);
            return updateable;
        }

        /// <summary>
        /// 修改
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="t"></param>
        /// <returns></returns>
        public IUpdateable<T> Updateable<T>(T t)
        {
            IUpdateable<T> updateable = _Updateable<T>();
            return updateable.Updateable(t);
        }

        /// <summary>
        /// 修改
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public IUpdateable<T> Updateable<T>()
        {
            IUpdateable<T> updateable = _Updateable<T>();
            return updateable.Updateable();
        }

        private IDeleteable<T> _Deleteable<T>()
        {
            IDeleteable<T> deleteable = InstanceFactory.CreateDeleteable<T>(_dbType, Database);
            return deleteable;
        }

        /// <summary>
        /// 删除
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public IDeleteable<T> Deleteable<T>()
        {
            IDeleteable<T> deleteable = _Deleteable<T>();
            return deleteable.Deleteable();
        }

        /// <summary>
        /// 根据主键删除
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="pks"></param>
        /// <returns></returns>
        public IDeleteable<T> Deleteable<T>(params object[] pks)
        {
            IDeleteable<T> deleteable = _Deleteable<T>();
            return deleteable.Deleteable(pks);
        }

        #region Queryable
        /// <summary>
        /// 查询
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public IQueryable<T> Queryable<T>()
        {
            IQueryable<T> queryable = InstanceFactory.CreateQueryable<T>(_dbType, Database);
            return queryable;
        }

        /// <summary>
        /// 多表连接，表1 别名 t1,表2 别名 t2
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <typeparam name="T2"></typeparam>
        /// <returns></returns>
        public IQueryable<T, T2> Queryable<T, T2>()
        {
            IQueryable<T, T2> queryable = InstanceFactory.CreateQueryable<T, T2>(_dbType, Database);
            return queryable;
        }

        public IQueryable<T, T2, T3> Queryable<T, T2, T3>()
        {
            IQueryable<T, T2, T3> queryable = InstanceFactory.CreateQueryable<T, T2, T3>(_dbType, Database);
            return queryable;
        }

        public IQueryable<T, T2, T3, T4> Queryable<T, T2, T3, T4>()
        {
            IQueryable<T, T2, T3, T4> queryable = InstanceFactory.CreateQueryable<T, T2, T3, T4>(_dbType, Database);
            return queryable;
        }

        public IQueryable<T, T2, T3, T4, T5> Queryable<T, T2, T3, T4, T5>()
        {
            IQueryable<T, T2, T3, T4, T5> queryable = InstanceFactory.CreateQueryable<T, T2, T3, T4, T5>(_dbType, Database);
            return queryable;
        }

        public IQueryable<T, T2, T3, T4, T5, T6> Queryable<T, T2, T3, T4, T5, T6>()
        {
            IQueryable<T, T2, T3, T4, T5, T6> queryable = InstanceFactory.CreateQueryable<T, T2, T3, T4, T5, T6>(_dbType, Database);
            return queryable;
        }

        public IQueryable<T, T2, T3, T4, T5, T6, T7> Queryable<T, T2, T3, T4, T5, T6, T7>()
        {
            IQueryable<T, T2, T3, T4, T5, T6, T7> queryable = InstanceFactory.CreateQueryable<T, T2, T3, T4, T5, T6, T7>(_dbType, Database);
            return queryable;
        }

        public IQueryable<T, T2, T3, T4, T5, T6, T7, T8> Queryable<T, T2, T3, T4, T5, T6, T7, T8>()
        {
            IQueryable<T, T2, T3, T4, T5, T6, T7, T8> queryable = InstanceFactory.CreateQueryable<T, T2, T3, T4, T5, T6, T7, T8>(_dbType, Database);
            return queryable;
        }

        public IQueryable<T, T2, T3, T4, T5, T6, T7, T8, T9> Queryable<T, T2, T3, T4, T5, T6, T7, T8, T9>()
        {
            IQueryable<T, T2, T3, T4, T5, T6, T7, T8, T9> queryable = InstanceFactory.CreateQueryable<T, T2, T3, T4, T5, T6, T7, T8, T9>(_dbType, Database);
            return queryable;
        }

        public IQueryable<T, T2, T3, T4, T5, T6, T7, T8, T9, T10> Queryable<T, T2, T3, T4, T5, T6, T7, T8, T9, T10>()
        {
            IQueryable<T, T2, T3, T4, T5, T6, T7, T8, T9, T10> queryable = InstanceFactory.CreateQueryable<T, T2, T3, T4, T5, T6, T7, T8, T9, T10>(_dbType, Database);
            return queryable;
        }

        public IQueryableUnion QueryableUnion()
        {
            return new QueryableUnionProvide(Database);
        }

        public IQueryable<T, T2> Queryable<T, T2>(IQueryable<T> query1, IQueryable<T2> query2)
        {
            IQueryable<T, T2> queryable = InstanceFactory.CreateQueryable<T, T2>(_dbType, Database);
            queryable.SetNestedQuery(query1, query2);
            return queryable;
        }
        #endregion Queryable

        public IDbFirst DbFirst()
        {
            var dbFirst = InstanceFactory.CreateInstance<IDbFirst>(_dbType.ToString() + "DbFirst", new object[] { Database });
            return dbFirst;
        }

        public ICodeFirst CodeFirst()
        {
            var codeFirst = InstanceFactory.CreateInstance<ICodeFirst>(_dbType.ToString() + "CodeFirst", new object[] { Database });
            return codeFirst;
        }

        public IBatch CreateBatchSubmitContext()
        {
            return new DefaultBatchImpl();
        }

        public void Dispose()
        {
            _Database?.Dispose();
        }
    }
}
